{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### pytest使用技巧\n",
    "在进行接口自动化测试时，pytest是一个非常成熟且易于上手的单元测试框架。相比于unittest,pytest更加灵活，功能更加强大。\n",
    "#### pytest简介\n",
    "pytest是一个用于编写和运行测试用例的开源框架，支持简单灵活的测试编写方式，能够与多种工具和库集成，如requests、selenium等，适用于接口自动化，web自动化、app自动化测试等。\n",
    "#### pytest的特性\n",
    "##### 1.与其他库的集成\n",
    "pytest可以与requests结合，用于发起接口请求；也可以与selenium集成，实现web自动化测试，此外还支持app自动化测试。\n",
    "##### 2.测试用例的跳过与失败重跑\n",
    "pytest提供了简单的方式来实现测试用例的跳过和失败重跑机制，这在unittest中相对复杂。\n",
    "##### 3.生成美观的测试报告\n",
    "通过与Allure等工具结合，pytest可以生成美观且易于分析的测试报告，方便展示结果和问题定位。\n",
    "##### 4.持续集成\n",
    "pytest可以与jenkins等持续集成工具集成，实现自动化的测试和部署流程。\n",
    "##### 5.丰富的插件支持\n",
    "pytest拥有大量的插件，可以扩展其功能，如pytest-html、pytest-xdist、pytest-rerunfailures\n",
    "\n",
    "#### 常用插件\n",
    "##### 1.pytest-html\n",
    "- 作用：生成html格式的测试报告\n",
    "##### 2.pytest-xdist\n",
    "- 作用：实现测试用例的分布式执行，支持多进程并发，提高测试效率\n",
    "##### 3.pytest-ordering\n",
    "- 作用：控制测试用例的执行顺序\n",
    "##### 4.pytest-rerunfailures\n",
    "- 作用：在测试用例失败后自动重跑，适用于临时性失败的用例\n",
    "##### 5.allure-pytest\n",
    "- 作用：与allure集成，生成美观的测试报告\n",
    "\n",
    "#### 插件安装\n",
    "使用pip安装所需的插件，例如：\n",
    "```bash\n",
    "pip install pytest-xdist\n",
    "```\n",
    "提示：如果安装速度较慢可以使用国内镜像源\n",
    "\n",
    "### pytest框架规则及运行方式\n",
    "在使用pytest进行测试之前，需要了解测试用例的编写规则和运行方式\n",
    "#### 测试用例的编写规则\n",
    "1.模块名称：测试模块（python文件）必须以test_开头或者以_test结尾。例如：\n",
    "```bash\n",
    "test_login.py\n",
    "login_test.py\n",
    "```\n",
    "2.测试类：测试类必须以Test开头，且不能包含__init__方法，例如：\n",
    "```python\n",
    "class Testlogin:\n",
    "    pass\n",
    "```\n",
    "3.测试方法：测试方法必须以test_开头。例如：\n",
    "```python\n",
    "def test_case01(self):\n",
    "    pass\n",
    "```\n",
    "\n",
    "#### 测试用例的运行方式\n",
    "pytest提供了多种运行测试用例的方式：\n",
    "##### 1.主函数运行方式\n",
    "在代码中调用pytest.main()，并可传入参数。例如：\n",
    "```python\n",
    "import pytest\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    pytest.main([\"-vs\", \"./test_cases/\", \"--reruns\", \"2\"])\n",
    "```\n",
    "常用参数：\n",
    "- -s: 显示打印输出等调试信息\n",
    "- -v: 详细模式，输出更详细的执行信息\n",
    "- -n: 指定并发线程数，需要安装pytest-xdist插件\n",
    "- --reruns: 指定失败重跑次数，需要安装pytest-rerunfailures插件\n",
    "##### 2.命令行运行方式\n",
    "直接在命令行使用pytest命令运行测试用例，例如：\n",
    "```bash\n",
    "pytest -vs ./test_cases/ --reruns 2\n",
    "```\n",
    "##### 3.使用pytest.ini配置文件运行\n",
    "在项目根目录下创建pytest.ini配置文件，定义测试运行的配置，例如：\n",
    "```shell\n",
    "[pytest]\n",
    "testpaths = ./test_cases\n",
    "python_files = test_*.py\n",
    "python_classes = Test*\n",
    "addopts = -vs --reruns 2\n",
    "```\n",
    "注意：\n",
    "- 文件名必须为pytest.ini\n",
    "- 文件编码格式应为utf-8，避免使用中文字符导致报错\n",
    "\n",
    "#### 指定测试用例的运行\n",
    "可以通过命令行参数指定运行的测试模块或目录：\n",
    "指定模块运行：\n",
    "```bash\n",
    "pytest ./test_cases/test_login.py\n",
    "```\n",
    "指定目录运行：\n",
    "```bash\n",
    "pytest ./test_cases/\n",
    "```\n",
    "\n",
    "#### 测试用例的分布式执行\n",
    "安装pytest-xdist插件，实现测试用例的多进程并发执行：\n",
    "```bash\n",
    "pip install pytest-xdist \n",
    "```\n",
    "使用方法：\n",
    "- 命令行：\n",
    "```bash\n",
    "pytest -vs ./test_cases/ -n 3\n",
    "```\n",
    "- 代码中：\n",
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    pytest.main([\"-vs\", \"./test_cases/\", \"-n\", \"3\"])\n",
    "```\n",
    "\n",
    "#### 改变测试用例的执行顺序\n",
    "安装pytest-ordering插件，使用装饰器控制执行顺序：\n",
    "```bash\n",
    "pip install pytest-ordering\n",
    "```\n",
    "示例：\n",
    "```python\n",
    "import pytest\n",
    "\n",
    "@pytest.mark.run(order=1)\n",
    "def test_case1():\n",
    "    pass\n",
    "\n",
    "@pytest.mark.run(order=2)\n",
    "def test_case2():\n",
    "    pass\n",
    "\n",
    "@pytest.mark.run(order=3)\n",
    "def test_case3():\n",
    "    pass\n",
    "```\n",
    "\n",
    "#### 测试用例失败重跑\n",
    "安装pytest-rerunfailures插件，实现失败后自动重跑：\n",
    "```bash\n",
    "pip install pytest-rerunfailures\n",
    "```\n",
    "使用方法：\n",
    "- 命令行：\n",
    "```bash\n",
    "pytest -vs ./test_cases/ --reruns 2\n",
    "```\n",
    "- 代码中：\n",
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    pytest.main([\"-vs\", \"./test_cases\", \"--reruns\", \"2\"])\n",
    "```\n",
    "\n",
    "#### 生成测试报告\n",
    "安装allure-pytest插件，与allure结合生成测试报告：\n",
    "```bash\n",
    "pip install allure-pytest\n",
    "```\n",
    "使用方法：\n",
    "```bash\n",
    "pytest -vs ./test_cases/ --alluredir ./report\n",
    "```\n",
    "生成并查看报告：\n",
    "```bash\n",
    "allure serve ./report\n",
    "```\n",
    "\n",
    "### pytest测试用例分组与跳过\n",
    "在实际测试中，可能需要对测试用例进行分组、跳过某些测试或有选择地执行特定的测试用例\n",
    "#### 分组测试用例\n",
    "##### 为什么要分组和分模块执行测试用例？\n",
    "在大型项目中，测试用例数量众多，分组可以方便地管理和执行特定的测试用例，如冒烟测试、功能模块测试等。\n",
    "通过分组，可以提高测试效率，节省时间。\n",
    "##### 使用pytest的标记功能进行分组\n",
    "1.为测试用例添加标记\n",
    "使用@pytest.mark.<marker_name>装饰器为测试用例添加自定义标记，例如：\n",
    "```python\n",
    "import pytest\n",
    "\n",
    "@pytest.mark.smoke\n",
    "def test_login_success():\n",
    "    pass\n",
    "\n",
    "@pytest.mark.user_management\n",
    "def test_add_user():\n",
    "    pass\n",
    "\n",
    "@pytest.mark.user_management\n",
    "def test_delete_user():\n",
    "    pass\n",
    "```\n",
    "2.在配置文件中注册标记\n",
    "为避免警告，需要在pytest.ini中注册自定义标记：\n",
    "```shell\n",
    "[pytest]\n",
    "markers = \n",
    "    smoke: 冒烟测试用例\n",
    "    user_management: 用户管理模块测试用例\n",
    "```\n",
    "3.执行特定标记的测试用例\n",
    "使用 -m 参数指定运行特定标记的测试用例\n",
    "- 执行所有冒烟测试用例：\n",
    "```bash\n",
    "pytest -m smoke\n",
    "```\n",
    "- 执行用户管理模块的测试用例：\n",
    "```bash\n",
    "pytest -m user_management\n",
    "```\n",
    "- 组合标记执行：\n",
    "```bash\n",
    "pytest -m \"smoke or user_management\"\n",
    "```\n",
    "- 仅执行同时属于冒烟测试和用户管理模块的测试用例：\n",
    "```bash\n",
    "pytest -m \"smoke and user_management\"\n",
    "```\n",
    "4.注意事项\n",
    "- 标记名需保持一致：标记名需要在装饰器、配置文件和命令行中保持一致\n",
    "- 导入pytest模块：在使用@pytest.mark时，需要先import pytest\n",
    "- 逻辑运算符：在命令行中使用逻辑运算符（and、or、not）组合标记时，需要用引号括起来\n",
    "\n",
    "#### 跳过测试用例\n",
    "##### 使用@pytest.mark.skip无条件跳过测试\n",
    "当某个测试用例暂时不需要执行时，可以使用@pytest.mark.skip装饰器：\n",
    "```python\n",
    "import pytest\n",
    "\n",
    "@pytest.mark.skip(reason=\"功能未实现\")\n",
    "def test_feature_x():\n",
    "    pass\n",
    "```\n",
    "解释：reason参数用于说明跳过原因\n",
    "##### 使用@pytest.mark.skipif有条件跳过测试\n",
    "根据条件决定是否跳过测试，用@pytest.mark.skipif装饰器：\n",
    "```python\n",
    "import pytest\n",
    "import sys\n",
    "\n",
    "@pytest.mark.skipif(sys.platform == \"win32\", reason=\"不支持windows平台\")\n",
    "def test_unix_only_feature():\n",
    "    pass\n",
    "```\n",
    "解释：当条件为True时，测试将被跳过\n",
    "##### 使用@pytest.skip()动态跳过测试\n",
    "在测试函数内部，根据运行时条件跳过测试：\n",
    "```python\n",
    "import pytest\n",
    "\n",
    "def test_dynamic_skip():\n",
    "    if not is_feature_available():\n",
    "        pytest.skip(\"该功能不可用\")\n",
    "    pass\n",
    "```\n",
    "解释：当跳过决策依赖于运行时信息时，这种方式非常有用\n",
    "\n",
    "#### 有效使用跳过和标记的技巧\n",
    "- 一致的标记命名：使用清晰、统一的标记名称，使测试选择更直观\n",
    "- 标记文档化：在 pytest.ini 中注册标记，避免警告并便于维护\n",
    "- 合理使用条件：在使用 skipif 时，条件应准确反映跳过的场景\n",
    "- 组合标记：利用标记组合，实现灵活的测试执行，适应不同的测试需求\n",
    "- 定期检查跳过的测试：避免长期跳过测试掩盖潜在问题，定期检查并更新"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
